package logika;

import grafika.Predplatitel;
import grafika.Vydavatel;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;

/**
 *  Trida Prostor - popisuje jednotlivé prostory (místnosti) hry
 *
 *  Tato třída je součástí jednoduché textové hry.
 *
 *  "Prostor" reprezentuje jedno místo (místnost, prostor, ..) ve scénáři
 *  hry. Prostor může mít sousední prostory připojené přes východy. Pro
 *  každý východ si prostor ukládá odkaz na sousedící prostor.
 *
 *@author     Michael Kolling, Lubos Pavlicek, Jarmila Pavlickova
 *@version    pro školní rok 2010/2011
 */
public class Prostor implements Vydavatel {

    private String nazev;
    private String popis;
    private Vec klic;
    private boolean zamcena = false;
    private boolean nebezpecna = false;
    private boolean hlidana = false;
    private Set<Prostor> vychody;   // obsahuje sousední místnosti
    private Collection<Vec> seznamVeci;
    private Collection<Postava> seznamPostav;

    /**
     * Vytvoření  prostoru se zadaným popisem, např.
     * "kuchyň", "hala", "trávník před domem"
     *
     *@param  nazev     nazev prostoru, jednoznačný identifikátor, jedno slovo nebo víceslovný název bez mezer.
     *@param  popis     Popis prostoru.
     */
    public Prostor(String nazev, String popis) {
        this.nazev = nazev;
        this.popis = popis;
        vychody = new HashSet<Prostor>();
        seznamVeci = new ArrayList<Vec>();
        seznamPostav = new ArrayList<Postava>();
    }

    /**
     * Definuje východ z prostoru (sousední/vedlejsi prostor). Vzhledem k tomu, že je použit
     * Set pro uložení východů, může být sousední prostor uveden pouze jednou
     * (tj. nelze mít dvoje dveře do stejné sousední místnosti). Druhé zadání
     * stejného prostoru tiše přepíše předchozí zadání (neobjeví se žádné chybové hlášení).
     * Lze zadat též cestu ze do sebe sama.
     * 
     * @param vedlejsi prostor, který sousedi s aktualnim prostorem.
     *
     */
    public void setVychod(Prostor vedlejsi) {
        vychody.add(vedlejsi);
    }

    /**
     * Metoda equals pro porovnání dvou prostorů. Překrývá se metoda equals ze třídy Object.
     * Dva prostory jsou shodné, pokud mají stejný název.
     * Tato metoda je důležitá z hlediska správného fungování seznamu východů (Set).
     * 
     * Bližší popis metody equals je u třídy Object.
     *
     *@param   o  object, který se má porovnávat s aktuálním
     *@return     hodnotu true, pokud má zadaný prostor stejný název, jinak false
     */
    @Override
    public boolean equals(Object o) {
        if (o instanceof Prostor) {
            Prostor druhy = (Prostor) o;
            return nazev.equals(druhy.nazev);
        } else {
            return false;
        }
    }

    /**
     * metoda hashCode vraci ciselny identifikator instance, ktery se pouziva pro optimalizaci ukladani
     * v dynamickych datovych strukturach. Pri prekryti metody equals je potreba prekryt i metodu hashCode.
     * Podrobny popis pravidel pro vytvareni metody hashCode je u metody hashCode ve tride Object
     */
    @Override
    public int hashCode() {
        return nazev.hashCode();
    }

    /**
     * Vrací název prostoru (byl zadán při vytváření prostoru jako
     * parametr konstruktoru)
     *
     *@return    název prostoru
     */
    public String getNazev() {
        return nazev;
    }

    /**
     * Vrací "dlouhý" popis prostoru, který může vypadat následovně:
     *      Jsi v mistnosti/prostoru vstupni hala budovy VSE na Jiznim meste.
     *      vychody: chodba bufet ucebna
     *
     *@return    Dlouhý popis prostoru
     */
    public String dlouhyPopis() {
        return "Jsi v prostoru zvaným " + nazev + popis + ".\n" + seznamVychodu();
    }

    /**
     * Vrací textový řetězec, který popisuje sousední východy)například:
     * "vychody: hala ".
     *
     *@return    Seznam názvů sousedních prostorů
     */
    private String seznamVychodu() {
        String vracenyText = "vychody:";
        for (Prostor sousedni : vychody) {
            vracenyText += " " + sousedni.getNazev();
            if (sousedni.jeZamceno()) {
                vracenyText += "(zamknuto)";
            }
            if (sousedni.jeNebezpecna()) {
                vracenyText += "(POZOR!!NEBEZPEČÍ!!)";
            }
        }
        return vracenyText;
    }

    /**
     * Vrací prostor, který sousedí s aktuálním prostorem a jehož název je zadán
     * jako parametr. Pokud prostor s udaným jménem nesousedí s aktuálním prostorem,
     * vrací se hodnota null.
     *
     *@param  nazevSouseda  Jméno sousedního prostoru (východu)
     *@return            Prostor, který se nachází za příslušným východem, nebo
     *                   hodnota null, pokud prostor zadaného jména není sousedem.
     */
    public Prostor vratSousedniProstor(String nazevSouseda) {
        if (nazevSouseda == null) {
            return null;
        }
        for (Prostor sousedni : vychody) {
            if (sousedni.getNazev().equals(nazevSouseda)) {
                return sousedni;
            }
        }
        return null;  // prostor nenalezen
    }

    /**
     * Vrací kolekci obsahující prostory, se kterými tento prostor sousedí.
     * Takto získaný seznam sousedních prostor nelze upravovat (přidávat, odebírat východy)
     * protože z hlediska správného návrhu je to plně záležitostí třídy Prostor.
     *
     *@return    Nemodifikovatelná kolekce prostorů (východů), se kterými tento prostor sousedí.
     */
    public Collection<Prostor> getVychody() {
        return Collections.unmodifiableCollection(vychody);
    }

    public Collection<Vec> getVeci() {
        return Collections.unmodifiableCollection(seznamVeci);

    }

    public Collection<Postava> getPostavy() {
        return Collections.unmodifiableCollection(seznamPostav);
    }

    /** 
     * Metoda nastavuje zda bude prostor zamčený či nikoli.
     * 
     * @param zamceno - proměnná boolean true nebo false.
     */
    public void zamknout(boolean zamceno) {
        this.zamcena = zamceno;
    }

    /**
     * Tato metoda vrací hodnotu podle toho,je-li prostor zamčen nebo odemčen.
     * 
     * @return true, pokud je prostor zamčený.
     */
    public boolean jeZamceno() {
        return zamcena;
    }

    /**
     * Obdoba metody zamknout(boolean zamceno).
     * 
     * @param nebezpecna - proměnná boolean true nebo false.
     */
    public void nebezpeci(boolean nebezpecna) {
        this.nebezpecna = nebezpecna;
    }

    /**
     * Obdoba metody jeZamceno().
     * 
     * @retrun true, pokud je prostor nebezpečný.
     */
    public boolean jeNebezpecna() {
        return nebezpecna;
    }

    /**
     * Další obdoba metody zamknout(boolean zamceno):D.
     * 
     * @param hlidana - proměnná boolean true nebo false.
     */
    public void hlidat(boolean hlidana) {
        this.hlidana = hlidana;
    }

    /**
     * Další obdoba metody jeZamceno().
     * 
     * @return true, pokud je protor hlídán.
     */
    public boolean jeHlidana() {
        return hlidana;
    }

    /**
     * Metoda nastavuje jaký klíč bude potřeba pro otevření nebo vstup do místnosti.
     * 
     * @param klic, instance tridy Vec.
     */
    public void setKlic(Vec klic) {
        this.klic = klic;
    }

    /**
     * Vrací klíč.
     * 
     * @return klíč který byl nasteven.
     */
    public Vec getKlic() {
        return klic;
    }

    /**
     * Metoda, která vytváří vkládá instance třídy Vec do seznamu a tím do místností ve hře.
     * 
     * @param neco, instance třídy Vec.
     */
    public void vlozVec(Vec neco) {
        seznamVeci.add(neco);
        upozorniMeNaZmenu();

    }

    /**
     * Metoda, jež procází seznam věcí v místnosti a pak jej vypíše.
     * 
     * @return vrací seznamVeci - řetězec String.
     */
    public String getSeznamVeci() {
        String veci = "";
        for (Vec neco : seznamVeci) {
            veci = veci + neco.getNazev() + " ";
        }
        return veci;

    }

    /**
     * Metoda, která odebere vec z místnosti (například když ji sebereme).
     * 
     * @param nazev - řetězec String.
     * @return instanci třídy Vec, která se jmenuje podle parametru.
     */
    public Vec odeberVec(String nazev) {
        Vec vec = null;
        for (Vec neco : seznamVeci) {
            if (neco.getNazev().equals(nazev)) {
                vec = neco;
                seznamVeci.remove(vec);
                upozorniMeNaZmenu();
                break;
            }
        }
        return vec;
    }

    /**
     * Metoda vrací tru pokud se v místnosti nachází věc s názvem jež se píše do parametru metody.
     * 
     * @param nazev, řetezec String.
     * @return true, pokud prostor obsahuje nějaké věci.
     */
    public boolean obsahujeVec(String nazev) {
        if (najdiVecVMistnosti(nazev) == null) { // vec není v místnosti
            for (Vec vec : seznamVeci) {    // prohledávám věci v místnosti
                if (vec.obsahujeVec(nazev)) { // když nějaká věc obsahuje věc
                    return true;
                }
            }
            return false;  // věc není ani v prozkoumaných věcech (truhlách)
        } else {
            return true;
        }
    }

    /**
     * Vložení postavy do místnosti.
     * 
     * @param nekdo, instance trídy Postava.
     */
    public void vlozPostavu(Postava nekdo) {
        seznamPostav.add(nekdo);
    }

    /**
     * Vrací seznam postav v místnosti.
     * 
     * @return seznam postav prostoru - řetězec String.
     */
    public String getSeznamPostav() {
        String lidi = "";
        if (seznamPostav.size() == 0) {
            lidi = "zadne";
        } else {
            for (Postava nekdo : seznamPostav) {
                lidi = lidi + nekdo.getJmeno() + " ";
            }
        }
        return lidi;
    }

    /**
     * Ekvivalent metody obsahujeVec(String nazev).
     * 
     * @param nazev - řetězec String.
     * @return true, pokud se hledaná postava v prsotoru nachází.
     */
    public boolean obsahujePostavu(String nazev) {
        boolean nalezena = false;
        for (Postava nekdo : seznamPostav) {
            if (nekdo.getJmeno().equals(nazev)) {
                nalezena = true;
                break;
            }
        }
        return nalezena;
    }

    /**
     * Ekvivalent metody najdiVecVMistnosti(String jmeno).
     *
     *@param nazev - retězec String.
     *@rerurn instanci třídy Postava, která odpovídá zadanému prarametru.
     */
    public Postava najdiPostavuVMistnosti(String jmeno) {
        Postava postava = null;
        for (Postava nekdo : seznamPostav) {
            if (nekdo.getJmeno().equals(jmeno)) {
                postava = nekdo;
                break;
            }
        }
        return postava;
    }

    public String getPopis() {
        return popis;
    }

    /**
     * Metoda prohledá seznam věcí v místnosti a snaží se najít jednu konkrétní věc se Stringovým jménem.
     * 
     *@param nazev - retězec String.
     *@rerurn instanci třídy Vec, která odpovídá zadanému prarametru.
     */
    public Vec najdiVecVMistnosti(String jmeno) {
        Vec vec = null;
        for (Vec neco : seznamVeci) {
            if (neco.getNazev().equals(jmeno)) {
                vec = neco;
                break;
            }
        }
        return vec;
    }
    private List<Predplatitel> predplatitele = new ArrayList<Predplatitel>();

    @Override
    public void zaregistruj(Predplatitel predplatitel) {
        predplatitele.add(predplatitel);
    }

    @Override
    public void odregistruj(Predplatitel predplatitel) {
        predplatitele.remove(predplatitel);
    }

    @Override
    public void upozorniMeNaZmenu() {
        for (Predplatitel predplatitel : predplatitele) {
            predplatitel.refresh();
        }
    }

    public List<Predplatitel> getPredplatitele() {
        return predplatitele;
    }
    
}
